home *** CD-ROM | disk | FTP | other *** search
/ Best Tools for JAVA / Best Tools for JAVA.iso / JAVA_ALL / IDE / SUBARTIC / SUB_ARCT / LIB / LISTBOX.JAV < prev    next >
Encoding:
Text File  |  1996-10-04  |  39.1 KB  |  1,167 lines

  1. package sub_arctic.lib;
  2. import sub_arctic.input.*;
  3. import sub_arctic.anim.timer;
  4. import sub_arctic.anim.timer_transition;
  5. import sub_arctic.anim.transition;
  6. import sub_arctic.anim.animation_agent;
  7.  
  8. import java.awt.Event;
  9. import java.util.Vector;
  10.  
  11. /**
  12.  * This class implements a style-nuetral listbox. This is the input
  13.  * behavior which is matched with the output behavior of listbox_display. <P>
  14.  * 
  15.  * 
  16.  * All callbacks pass the currently focused on object as their data
  17.  * to the callback. Summary of the callback behavior of this object: <P>
  18.  * <DL>
  19.  * <DT> SINGLE_CLICK_PENDING
  20.  * <DD> This callback is sent when the mouse is first released from an
  21.  * object. This is the "early" callback which precedes either a
  22.  * SINGLE_CLICK_FINAL or DOUBLE_CLICK.
  23.  * <DT> SINGLE_CLICK_FINAL
  24.  * <DD> This is the callback that is sent when the listbox is sure that
  25.  * no double click will be forthcoming. This may be sent in response to
  26.  * a timeout, or may be sent immediately following the SINGLE_CLICK_PENDING
  27.  * in cases where a double click is not allowed.
  28.  * <DT> DOUBLE_CLICK
  29.  * <DD> This is sent when a double click is detected on a particular object.
  30.  * It will always be preceeded by a SINGLE_CLICK_PENDING callback.
  31.  * <DT> DRAG
  32.  * <DD> This message is sent when the user depresses the mouse on the
  33.  * focused object and then moves the mouse some number of pixels (default
  34.  * of 4 pixels).  This message is only sent when you have previously 
  35.  * called set_allow_dragging(true). 
  36.  * </DL>
  37.  * 
  38.  * @author Ian Smith
  39.  * @version $Id: listbox.java,v 1.12 1996/10/04 23:03:27 iansmith Exp $
  40.  */
  41. public class listbox extends listbox_display implements simple_draggable, pressable, timer {
  42.   /**
  43.    * This use of this  constant means that listboxes in this group
  44.    * will have their content synchronized.
  45.    */
  46.   public static final int SYNCH_CONTENT=1<<0;
  47.   /**
  48.    * This use of this  constant means that listboxes in this group
  49.    * will have their selections synchronized.
  50.    */
  51.   public static final int SYNCH_SELECTION=1<<1;
  52.   /**
  53.    * This use of this  constant means that listboxes in this group
  54.    * will have their focused objects synchronized.
  55.    */
  56.   public static final int SYNCH_FOCUSED=1<<2;
  57.   /**
  58.    * This use of this  constant means that listboxes in this group
  59.    * will have their displayed x coordinates synchronized.
  60.    */
  61.   public static final int SYNCH_X_COORD=1<<3;
  62.   /**
  63.    * This use of this  constant means that listboxes in this group
  64.    * will have their displayed y coordinates synchronized.
  65.    */
  66.   public static final int SYNCH_Y_COORD=1<<4;
  67.   /**
  68.    * This mask holds what the current set of synchronization constants
  69.    * are. This defaults to synchronizing the y coordinates, the selected
  70.    * set and the focused object; this set of synchronizations is good
  71.    * for viewing database records.
  72.    */
  73.   protected int _synch_mask = SYNCH_SELECTION | SYNCH_FOCUSED | SYNCH_Y_COORD;
  74.   
  75.   /**
  76.    * This variable holds whether or not we are allowed to have multiple
  77.    * selections.
  78.    */
  79.   protected boolean _single_selection=false;
  80.   /**
  81.    * This constant holds the callback number for double click.
  82.    * When you get a double click callback the parameter passed to the 
  83.    * callback function is the element which was double clicked on. It is
  84.    * returned via the convert_to_object() part of the list_element
  85.    * interface.
  86.    */
  87.   public static final int DOUBLE_CLICK=0;
  88.   /**
  89.    * This constant holds the callback number for a drag.
  90.    * When you get a drag callback the parameter passed to the callback
  91.    * function is the element which was dragged. It is
  92.    * returned via the convert_to_object() part of the list_element
  93.    * interface.
  94.    */
  95.   public static final int DRAG=1;
  96.   /**
  97.    * This constant holds the callback number for a tentative
  98.    * first click action. When this callback is received, it
  99.    * may be the case that a double click will follow this 
  100.    * callback. If no double click actually occurs, then a
  101.    * SINGLE_CLICK_FINAL call will be forthcoming.<P>
  102.    *
  103.    * The object passed to the callback function is the
  104.    * currently focused on item.
  105.    */
  106.   public static final int SINGLE_CLICK_PENDING=2;
  107.   /**
  108.    * This constant holds the callback number for a a single
  109.    * click. When this call is made, there is no chance that
  110.    * a double click will result from this first click, thus
  111.    * actions which need to be sure that no double click can
  112.    * occur should handle this callback rather than 
  113.    * SINGLE_CLICK_PENDING.<P>
  114.    * 
  115.    * The object passed as the parameter to the callback function
  116.    * when this callback is run is the currently focused on
  117.    * object. The event field of the callback may be an 
  118.    * animation event 
  119.    * when this callback is run because this callback can be
  120.    * run not response to a user event, but rather a timeout.
  121.    */
  122.   public static final int SINGLE_CLICK_FINAL=3;
  123.   /**
  124.    * Return whether or not we are only allowing single selections.
  125.    *
  126.    * @return boolean true if this listbox will only allow single selections
  127.    */
  128.   public boolean single_selection() { return _single_selection;}
  129.   /**
  130.    * This function changes the selection mode for this object. Calling
  131.    * this function will result in set of selections being cleared.
  132.    * @param boolean s true if you want to allow only single selections
  133.    */
  134.   public void set_single_selection(boolean s) {
  135.     clear_selected();
  136.     _single_selection=s;
  137.   }
  138.   /**
  139.    * This storage is used by the interactor to know if a double click
  140.    * might be coming. If this variable is null, there is no transition
  141.    * scheduled, and thus we are not waiting on a possible double click.
  142.    */
  143.   protected transition _double_click_trans;
  144.   /**
  145.    * This is how many milliseconds to wait for a double click.
  146.    */
  147.   protected int _double_click_delay=350;
  148.   /**
  149.    * This is what the user originally clicked on, thus where we
  150.    * want to check to see if they are if we get a fast second click.
  151.    */
  152.   protected int _double_click_loc;
  153.   /**
  154.    * This variable determines who the next listbox is in the circularly
  155.    * linked list of listboxes we are synchronizing together. If you
  156.    * don't use groups, this will always point to this object.
  157.    */
  158.   protected listbox next = this;
  159.   /**
  160.    * This value is the value of the currently focused on object.
  161.    * This object may or may not be in the selected set. 
  162.    */
  163.   protected int _focused=-1;
  164.   /**
  165.    * This variable is used to store the x position that began a drag.
  166.    * If the DRAG callback is going to be called, the listbox needs to
  167.    * know when the user has dragged DRAG_DISTANCE from the origin of
  168.    * the drag.<P>
  169.    * 
  170.    * This variable (and its y counterpart) wouldn't need to be here
  171.    * if we were using a different drag protocol, as it would give us
  172.    * the starting x and y coordinate. However, I felt that
  173.    * it would add needless complexities to the code use a different
  174.    * protocol since that protocol would only be utilized in one place,
  175.    * but would be visible all over the code.
  176.    */
  177.   private static int drag_x_origin;
  178.   /**
  179.    * This variable is used to store the y position that began a drag.
  180.    * If the DRAG callback is going to be called, the listbox needs to
  181.    * know when the user has dragged DRAG_DISTANCE from the origin of
  182.    * the drag.
  183.    */
  184.   private static int drag_y_origin;
  185.   /**
  186.    * This boolean is true if a drag has started which might require
  187.    * a DRAG callback.
  188.    */
  189.   private static boolean drag_in_progress=false;
  190.   /**
  191.    * This boolean is true if the drag message has already been sent
  192.    * the user level code. 
  193.    */
  194.   private static boolean drag_message_sent=false;
  195.   /**
  196.    * This is the variable that holds the index of the object we clicked
  197.    * on when we possibly are beginning a drag. Since the feedback to
  198.    * the user might need to be different, we can't guarantee that
  199.    * this is the focused object when the drag starts. Thus, we have
  200.    * to keep this variable to store the focused on object.
  201.    */
  202.   private static int drag_focus_index;
  203.   /**
  204.    * This is the distance that the user must drag the focused object
  205.    * in either X or Y before the system will send a drag message.
  206.    */
  207.   protected int DRAG_DISTANCE=4;
  208.   /**
  209.    * This is the callback object for this listbox.
  210.    */
  211.   protected callback_object _callback_obj=null;
  212.   /**
  213.    * Retreive the callback object for this listbox.
  214.    * @return callback_object the object handling callbacks from this listbox
  215.    */
  216.   public callback_object callback_obj() { return _callback_obj;}
  217.   /**
  218.    * Set the callback object for this listbox.
  219.    * @param callback_object co the new callback object
  220.    */
  221.   public void set_callback_obj(callback_object co) {
  222.     _callback_obj=co;
  223.   }
  224.   /**
  225.    * This boolean should be set to true if you want your object
  226.    * to be able to drag objects out of its list. This defaults to
  227.    * false.
  228.    */
  229.   protected boolean _allow_dragging=false;
  230.   /**
  231.    * Retreive the current state of draggability. If this is true,
  232.    * then objects may be dragged out of this list.
  233.    * @return boolean true if objects may be dragged
  234.    */
  235.   public boolean allow_dragging() { return _allow_dragging;}
  236.   /**
  237.    * Set the current state of dragging. If you set this to true,
  238.    * you should be prepared to receive DRAG callbacks and further
  239.    * you should act on them when they are received. In particular,
  240.    * you should remove this listbox from the focus set of the
  241.    * simple_drag_object. <P>
  242.    *
  243.    * Although we are allowing you to not really handle a drag from
  244.    * the list (based on some condition of your choosing), users
  245.    * may become confused when they attempt to change their selection
  246.    * in the listbox and nothing happens. This is because the state
  247.    * machine for doing selection is not "turned on" when the listbox
  248.    * is expecting you to respond to a DRAG callback.
  249.    * @param boolean a the new state of dragging
  250.    */
  251.   public void set_allow_dragging(boolean a) {_allow_dragging=a;}
  252.   /**
  253.    * This variable is used to allow the system to know where the
  254.    * last valid object was before the user's mouse went "out of
  255.    * bounds."
  256.    */
  257.   protected int _last_valid;
  258.  
  259.  
  260.   /**
  261.    * Return the currently focused on object. This object may or 
  262.    * may not be in the set of selected objects. This object will
  263.    * be returned to you via the "convert_to_object" method of
  264.    * the list element. You will get a value of null if no object
  265.    * currently is focused. 
  266.    * @return int the index of the currently focused object
  267.    */
  268.   public Object focused() { 
  269.     list_element el;
  270.     Object obj;
  271.     /* safety check */
  272.     if (_focused==-1) return null;
  273.     /* retrieve the element */
  274.     el=(list_element)_child_display.child(_focused);
  275.     /* get the object */
  276.     obj=el.convert_to_object();
  277.     /* if its not null, return it */
  278.     if (obj!=null) return obj;
  279.     /* otherwise return the object itself */
  280.     return el;
  281.   }
  282.   /**
  283.    * Return the currently focused on object as a list_element. 
  284.    * This object may or may not be in the set of selected objects. 
  285.    * You will get a value of null if no object currently is focused. 
  286.    *
  287.    * @return int the index of the currently focused object
  288.    */
  289.   public list_element focused_raw() { 
  290.     list_element el;
  291.  
  292.     /* safety check */
  293.     if (_focused==-1) return null;
  294.     /* retrieve the element */
  295.     el=(list_element)_child_display.child(_focused);
  296.     /* send it home */
  297.     return el;
  298.   }
  299.  
  300.   /**
  301.    * This function sets the focused on object to a given object
  302.    * index. If you give a bad index value, no object ends up being
  303.    * the focus.
  304.    *
  305.    * @param int indx the index of the object that you wish to become the focus
  306.    */
  307.   public void set_focused(int indx) {
  308.     list_element old,new_focus;
  309.     
  310.     if ((synch_mask() & SYNCH_FOCUSED) !=0) {
  311.       /* we need to do it */
  312.       if (synch_in_progress) {
  313.     /* end of the road for this puppy */
  314.     return;
  315.       }
  316.       /* need to remember where we have been */
  317.       synch_in_progress=true;
  318.       /* tell the next guy */
  319.       next.set_focused(indx);
  320.       /* we are done synchronizing */
  321.       synch_in_progress=false;
  322.     }
  323.     /* is there one? */
  324.     if (_focused!=-1) {
  325.       old=(list_element)_child_display.child(_focused);
  326.       /* did it somehow get changed */
  327.       if (old.focused()) {
  328.     old.lost_focus();
  329.       }
  330.     }
  331.     /* is there a new focus? */
  332.     if (indx!=-1) {
  333.       /* there is a new focus in town */
  334.       new_focus=(list_element)_child_display.child(indx);
  335.       /* make it the focus and remember that */
  336.       new_focus.become_focus();
  337.     }
  338.     /* keep track */
  339.     _focused=indx;
  340.     return;
  341.   }
  342.   /**
  343.    * This object is the current "starting point" for a drag interactor.
  344.    * This start point is the one around which all drags occur; it is
  345.    * the balancing point for a drag. It is only valid during a drag
  346.    * and only when the selection mode allows multiple selections.
  347.    */
  348.   protected int _start;
  349.   /**
  350.    * This is the index of the object we were over the last time we
  351.    * got any input during a drag sequence. This value may be -1 if we
  352.    * weren't over any object before (we were out of bounds).  
  353.    */
  354.   protected int _prev;
  355.   /**
  356.    * This function can derive an index position of a list_element
  357.    * from an event in this object's coordinate system.
  358.    * @param event evt the event we are interested in
  359.    * @return int the index number of the element or -1 if not on any element
  360.    */
  361.   public int index_for_event(event evt) {
  362.     event e=new event(evt);
  363.     /* transform the coordinate systems down */
  364.     e.global_to_local(_child_display);
  365.     if ((e.local_x() <0) || (e.local_y()<0) ||
  366.     (e.local_x()>=_child_display.w()) || 
  367.     (e.local_y()>=_child_display.h())) {
  368.       return -1;
  369.     }
  370.     /* convert to an index value */
  371.     return _child_display.index_for_pixel(e.local_y());
  372.   }
  373.   /**
  374.    * Construct a listbox of a given size.  We default to always
  375.    * having a vertical scrollbar and never having a horizontal one.
  376.    * 
  377.    *
  378.    * @param int w the width of the listbox in pixels
  379.    * @param int h the height of the listbox in pixels
  380.    * @param boolean s true if you want to allow only single selections
  381.    * @param callback_object co the callback object for this listbox
  382.    */
  383.   public listbox(int w, int h, boolean s,callback_object co) 
  384. {
  385.     super(w,h);
  386.     _single_selection=s;
  387.     _callback_obj=co;
  388.     /* set the status */
  389.     set_vertical_status(listbox_display.SCROLLBAR_ALWAYS);
  390.     set_horizontal_status(listbox_display.SCROLLBAR_NEVER);
  391.  
  392.   }
  393.  
  394.    //had:
  395.    //* @exception general PROPAGATED
  396.  
  397.   /**
  398.    * Start a drag. This just sends the start drag message to one of
  399.    * the appropriate handlers for the selection mode this box is in.
  400.    * @param event evt the event in question
  401.    * @param Object user_info (not used)
  402.    */
  403.   public boolean drag_start(event evt, Object user_info) {
  404.     if (_single_selection) {
  405.       return(drag_start_one(evt,user_info));
  406.     } else {
  407.       return(drag_start_multiple(evt,user_info));
  408.     }
  409.   }
  410.   /**
  411.    * Continue a drag. This just sends the drag_feedback message to one of
  412.    * the appropriate handlers for the selection mode this box is in.
  413.    * @param event evt the event in question
  414.    * @param Object user_info (not used)
  415.    */
  416.   public boolean drag_feedback(event evt, Object user_info) {
  417.     if (_single_selection) {
  418.       return(drag_feedback_one(evt,user_info));
  419.     } else {
  420.       return(drag_feedback_multiple(evt,user_info));
  421.     }
  422.   }
  423.   /**
  424.    * End a drag. This just sends the drag_end message to one of
  425.    * the appropriate handlers for the selection mode this box is in.
  426.    * @param event evt the event in question
  427.    * @param Object user_info (not used)
  428.    */
  429.   public boolean drag_end(event evt, Object user_info) {
  430.     if (_single_selection) {
  431.       return(drag_end_one(evt,user_info));
  432.     } else {
  433.       return(drag_end_multiple(evt,user_info));
  434.     }
  435.   }
  436.   /**
  437.    * Start a drag for a listbox which is in single selection mode.
  438.    * 
  439.    * @param event evt the event in question
  440.    * @param Object user_info (not used)
  441.    */
  442.   public boolean drag_start_one(event evt, Object user_info) {
  443.     int loc=index_for_event(evt);
  444.  
  445.     /* is there a drag going on? */
  446.     if (drag_in_progress) {
  447.       /* there is a drag going on, bail out */
  448.       return true;
  449.     }
  450.     /* normal case: clear the selection and add this object to
  451.      * the selected set */
  452.     clear_selected();
  453.     add_to_selected_set(loc,loc);
  454.     return true;
  455.   }
  456.   /**
  457.    * Continue a drag for a listbox which is in single selection mode.
  458.    * 
  459.    * @param event evt the event in question
  460.    * @param Object user_info (not used)
  461.    */
  462.   public boolean drag_feedback_one(event evt, Object user_info) {
  463.     int loc=index_for_event(evt);
  464.     
  465.     /* are they doing a drag? */
  466.     if (drag_in_progress) {
  467.       /* have they dragged enough ?*/
  468.       if (drag_ready(evt.local_x(), evt.local_y())) {
  469.     /* if the user has already gotten a drag message, the
  470.        drag hook code won't send it again */
  471.     drag_hook(evt,user_info);
  472.       } 
  473.       /* if there is a drag, we are done */
  474.       return true;
  475.     }
  476.     /* make sure that if they are out of bounds, we don't do anything */
  477.     if (loc!=-1){ 
  478.       /* normally, we just clear the current selection and reset to
  479.      the object under the cursor */
  480.       clear_selected();
  481.       /* select the right objects */
  482.       add_to_selected_set(loc,loc);
  483.       /* update the last valid location */
  484.       _last_valid=loc;
  485.     }
  486.     return true;
  487.   }
  488.   /**
  489.    * End a drag for a listbox which is in single selection mode.
  490.    * 
  491.    * @param event evt the event in question
  492.    * @param Object user_info (not used)
  493.    */
  494.   public boolean drag_end_one(event evt, Object user_info) {
  495.     int loc=index_for_event(evt);
  496.  
  497.     /* is the user doing a drag? */
  498.     if (drag_in_progress) {
  499.       /* no matter what happens here, we need to reset the state */
  500.       drag_in_progress=false;
  501.       /* there are two cases in which the drag message 
  502.      sent might be false... */
  503.       if (drag_message_sent==false) {
  504.     /* first one is that the distance travelled is too small... */
  505.     if (drag_ready(evt.local_x(), evt.local_y())==true) {
  506.       /* its not too small, so the user is telling us to get
  507.          out of the way, so we will ... i.e. this call is in
  508.          response to the user  removing us from the selected set*/
  509.       return true;
  510.     }
  511.     /* this case is the one where the user just clicked on the
  512.        focused item... we can just run the code for the normal
  513.        case and recover */
  514.       } else {
  515.     /* we have sent the drag message and the user-level code didn't
  516.        handle it for some reason... we are just going to ignore this
  517.        input, really this probably an error */
  518.     return true;
  519.       }
  520.     }
  521.     /* if they are out of bounds, just substitute the last valid
  522.        place they were */
  523.     if (loc==-1) {
  524.       loc=_last_valid;
  525.     }
  526.     /* normal case: clear the selection make this object the
  527.        only thing in the selected set */
  528.     clear_selected();
  529.     add_to_selected_set(loc,loc);
  530.     /* start a timer */
  531.     _double_click_trans=new timer_transition(this,_double_click_delay);
  532.     manager.animation.schedule_transition(_double_click_trans);
  533.     /* remember where he clicked */
  534.     _double_click_loc=loc;
  535.     /* set the focus */
  536.     set_focused(loc);
  537.     /* run the callback */
  538.     single_click_pending_hook(evt);
  539.     return true;
  540.   }
  541.   /**
  542.    * Start a drag for a listbox which is in multiple selection mode.
  543.    * 
  544.    * @param event evt the event in question
  545.    * @param Object user_info (not used)
  546.    */
  547.   public boolean drag_start_multiple(event evt, Object user_info) {
  548.     int loc=index_for_event(evt);
  549.  
  550.     /* is it shifted? */
  551.     if ((evt.modifiers()  & Event.SHIFT_MASK) !=0 ) {
  552.       /* do they have a current object?... if not give them one*/
  553.       if (_focused==-1) {
  554.     set_focused(loc);
  555.       }
  556.       /* make the focused object be the start */
  557.       _start=_focused;
  558.     } else {
  559.       /* do they have the control down? */
  560.       if ((evt.modifiers()  & Event.CTRL_MASK) !=0) {
  561.     _start=loc;
  562.       } else {
  563.     /* if there is a drag in progress, we don't do anything on
  564.          start */
  565.     if (!drag_in_progress) {
  566.       clear_selected();
  567.       _start=loc;
  568.     } else {
  569.       /* it is progress, bail out of here */
  570.       return true;
  571.     } 
  572.       }
  573.     }
  574.     /* we now have start set up the way we want ... just the right range*/
  575.     add_to_selected_set(_start,loc);
  576.     /* we want to know where we have been */
  577.     _prev=loc;
  578.     /* done */
  579.     return true;
  580.   }
  581.   /**
  582.    * Continue a drag for a listbox which is in multiple selection mode.
  583.    * 
  584.    * @param event evt the event in question
  585.    * @param Object user_info (not used)
  586.    */
  587.   public boolean drag_feedback_multiple(event evt, Object user_info) {
  588.     int loc=index_for_event(evt);
  589.     int large_x, small_x, large_y, small_y;
  590.     
  591.     /*if there is a drag in progress, there is only one thing to
  592.     do... see if they dragged far enough ..*/
  593.     if (drag_in_progress) { /* are they in a drag? */
  594.       /* have they dragged enough ?*/
  595.       if (drag_ready(evt.local_x(), evt.local_y())) {
  596.     /* if the user has already gotten a drag message, the
  597.        drag hook code won't send it again */
  598.     drag_hook(evt,user_info);
  599.       } 
  600.       /* if there is a drag, we are done */
  601.       return true;
  602.     }
  603.     /* if they go out of bounds, we don't do anything */
  604.     if (loc==-1) {
  605.       return true;
  606.     }
  607.     /* in bounds... are they in the same object? */
  608.     if (loc==_prev) {
  609.       /* they are, bail out */
  610.       return true;
  611.     }
  612.     /* new object might not be visible */
  613.     ensure_visible(loc);
  614.  
  615.     /* they are somewhere else... were they in bounds before? */
  616.     if (_prev!=-1) {
  617.       /* they were in bounds before ... remove it from the selected set*/
  618.       remove_from_selected_set(_prev,loc);
  619.     }
  620.     /* set the new objects into the set */
  621.     add_to_selected_set(_start,loc);
  622.     /* make sure we remember where we were */
  623.     _prev=loc;
  624.     /* done */
  625.     return true;
  626.   }
  627.   /**
  628.    * End a drag for a listbox which is in multiple selection mode.
  629.    * 
  630.    * @param event evt the event in question
  631.    * @param Object user_info (not used)
  632.    */
  633.   public boolean drag_end_multiple(event evt, Object user_info) {
  634.     int loc=index_for_event(evt);
  635.  
  636.     /* is the user doing a drag? */
  637.     if (drag_in_progress) {
  638.       /* no matter what happens here, we need to reset the state */
  639.       drag_in_progress=false;
  640.       /* there are two cases in which the drag message 
  641.      sent might be false... */
  642.       if (drag_message_sent==false) {
  643.     /* first one is that the distance travelled is too small... */
  644.     if (drag_ready(evt.local_x(), evt.local_y())==true) {
  645.       /* its not too small, so the user is telling us to get
  646.          out of the way, so we will ... i.e. this call is in
  647.          response to the user  removing us from the selected set*/
  648.       return true;
  649.     }
  650.     /* if we get here, the user has simply clicked on focused
  651.        item... we need to get back into a state in which
  652.        the normal code can run ... so we replicate the code that
  653.        should have run at start time */
  654.     clear_selected();
  655.     _start=drag_focus_index; /* don't use loc: it might be -1! */
  656.     _prev=drag_focus_index;
  657.     /* add this object to the selected set */
  658.     add_to_selected_set(_start,_start);
  659.       } else {
  660.     /* we have sent the drag message and the user-level code didn't
  661.        handle it for some reason... we are just going to ignore this
  662.        input, really this probably an error */
  663.     return true;
  664.       }
  665.     }
  666.     /* do the normal feedback */
  667.     drag_feedback_multiple(evt,user_info);
  668.     /* now just set the focus to be where they left off */
  669.     set_focused(_prev);
  670.     /* run the callback for the release */
  671.     single_click_pending_hook(evt);
  672.     /* did the user drag out a region?*/
  673.     if (loc==_start) {
  674.       /* he didn't drag out a region, start a timer going */
  675.       _double_click_trans=new timer_transition(this,_double_click_delay);
  676.       manager.animation.schedule_transition(_double_click_trans);
  677.       /* remember where he clicked */
  678.       _double_click_loc=loc;
  679.     } else {
  680.       /* we know there is not going to be a double click so go ahead
  681.        * and send the message about the first click ending */
  682.       single_click_final_hook(evt);
  683.     }
  684.     return true;
  685.   }
  686.   /**
  687.    * Handle the mouse button being depressed on our area.
  688.    * @param event evt the event in question
  689.    * @param Object user_info (not used)
  690.    */
  691.   public boolean press(event evt, Object user_info) {
  692.     int loc=index_for_event(evt);
  693.     interactor drag_obj;
  694.  
  695.     /* is it in bounds? */
  696.     if (loc==-1) {
  697.       return false;
  698.     }
  699.     /* update the last valid location */
  700.     _last_valid=loc;
  701.  
  702.     /* check for possible double click */
  703.     if (_double_click_trans!=null) {
  704.       /* we know we got another click in time */
  705.       manager.animation.remove_transition(_double_click_trans);
  706.       /* is it in the same place? */
  707.       if (loc==_double_click_loc) {
  708.     /* it is in the same place, run the callback */
  709.     double_click_hook(evt,user_info);
  710.     /* remove the transition */
  711.     _double_click_trans=null;
  712.     /* we are done here */
  713.     return true;
  714.       }
  715.     }
  716.     /* is the user trying to pick up the focused object */
  717.     if (_focused==loc) {
  718.       /* ok, the user started out on the object that is focused,
  719.      a drag may be needed ... make sure the cntrl and shift
  720.      are not depressed */
  721.       if (allow_dragging() &&
  722.       ((evt.modifiers() & Event.SHIFT_MASK) ==0) &&
  723.       ((evt.modifiers() & Event.CTRL_MASK)==0)) {
  724.     /* we are in a drag and have not yet sent the drag message */
  725.     drag_in_progress=true;
  726.     drag_message_sent=false;
  727.     /* store the location of the start of the drag  */
  728.     drag_x_origin=evt.local_x();
  729.     drag_y_origin=evt.local_y();
  730.     /* store the index of the focus, since the default start
  731.        behavior is to clear the focus */
  732.     drag_focus_index=_focused;
  733.       }
  734.     }
  735.     manager.simple_drag_focus.set_focus_to(this,evt,null);
  736.     return true;
  737.   }
  738.   /**
  739.    * Handle the mouse button being released on our area. This never
  740.    * happens, as we use a focus based agent which short circuits
  741.    * the input.
  742.    * @param event evt the event in question
  743.    * @param Object user_info (not used)
  744.    */
  745.   public boolean release(event evt, Object user_info) {
  746.     return false;
  747.   }
  748.   /**
  749.    * We don't allow ourselves to ever be picked directly. The 
  750.    * listbox_helper object will put us in the pick list <I>only</I>
  751.    * in the case where the main display area was picked on.
  752.    *
  753.    * @param int pt_x the x coordinate of the point
  754.    * @param int pt_y the y coordinate of the point
  755.    * @param pick_collector the object to put yourself in if you are picked
  756.    */
  757.   public void pick(int pt_x, int pt_y, pick_collector pick_list) 
  758. {
  759.     /* just go into the children */
  760.     pick_within_children(pt_x, pt_y, pick_list);
  761.   }   
  762.  
  763.    //had:
  764.    //* @exception general PROPAGATED
  765.  
  766.   /**
  767.    * Generate a little different string for the listbox.
  768.    * @return String a simple dump of this object
  769.    */
  770.   public String toString() {
  771.       StringBuffer result = new StringBuffer();
  772.  
  773.       /* Do a minimal dump */
  774.       result.append("listbox:[x="); result.append(x());
  775.       result.append(", y="); result.append(y());
  776.       result.append(", w="); result.append(w());
  777.       result.append(", h="); result.append(h());
  778.       result.append("]");
  779.  
  780.       return result.toString();
  781.   }
  782.   /**
  783.    * Add this listbox to a group of listboxes which have (possibly)
  784.    * synchronized input and output. 
  785.    * @param listbox a listbox in the group you wish this listbox to be added to
  786.    */
  787.   public void add_to_group(listbox l) {
  788.     listbox points_to_l;
  789.  
  790.     /* we want points_to_l to be a listbox which points to l */
  791.     if (l.next==l) {
  792.       points_to_l=l;
  793.     }
  794.     else {
  795.       points_to_l=l.find_sibling_of(l);
  796.     }
  797.     /* link us in to that circularly linked list */
  798.     next=l;
  799.     points_to_l.next=this;
  800.   }
  801.   /**
  802.    * This is a utility function which finds the object in a linked list
  803.    * which points to some target listbox.
  804.    * @param listbox target the listbox whose sibling you want to find
  805.    * @return listbox the listbox that points to the target
  806.    */
  807.   protected listbox find_sibling_of(listbox target) {
  808.  
  809.     /* try this one... */
  810.     if (next==target) {
  811.       return this;
  812.     }
  813.     /* go to the next one */
  814.     /* assumed invariant: Circularly linked lists will always find target */
  815.     return (next.find_sibling_of(target));
  816.   }
  817.   /**
  818.    * This function will cause a listbox to be removed from the group
  819.    * it is currently in and return to its own group (of one).
  820.    */
  821.   public void remove_from_group() {
  822.  
  823.     /* if you aren't in a group, this does nothing */
  824.     if (next==this) return;
  825.     /* otherwise: find who points at us */
  826.     listbox sib=find_sibling_of(this);
  827.     /* tell them to adjust their pointers */
  828.     sib.next=next;
  829.     /* we are now in our own group */
  830.     next=this;
  831.   }
  832.   /**
  833.    * Retreive the current mask of synchronization values.
  834.    * @return int a bitmask representing the state of the synchronization for this listbox
  835.    */
  836.   public int synch_mask() {  return _synch_mask;}
  837.   /**
  838.    * Set the synchronization mask to a new value. You should OR together
  839.    * the set of flags that you want to be in the mask from the set of
  840.    * SYNCH_X_COORD, SYNCH_Y_COORD, SYNCH_FOCUSED, SYNCH_SELECTION, and
  841.    * SYNCH_CONTENT.
  842.    * @param int mask the new synchronization mask
  843.    */
  844.   public void set_synch_mask(int mask) {
  845.     _synch_mask=mask;
  846.   }
  847.   /**
  848.    * This boolean is true when we are in the process of performing a 
  849.    * synchronization activity.  This prevents us from getting confused
  850.    * because of the circularly linked list.
  851.    */
  852.   protected boolean synch_in_progress=false;
  853.   /**
  854.    * We override the set selected method so we can synchronize listboxes.
  855.    * Be that synchronized selected objects are synchronized in number
  856.    * not in content. If you want synchronized content you have to 
  857.    * use SYNCH_CONTENT.
  858.    *
  859.    * @param Vector selected the objects which are now to be selected
  860.    */
  861.   public void set_selected(Vector selected) {
  862.     Vector indexes=new Vector();
  863.     int index,i;
  864.     interactor obj;
  865.  
  866.     if ((synch_mask() & SYNCH_SELECTION) !=0) {
  867.       /* we need to do it */
  868.       if (synch_in_progress) {
  869.     /* end of the road for this puppy */
  870.     return;
  871.       }
  872.       /* need to remember where we have been */
  873.       synch_in_progress=true;
  874.       /* if selected is empty, we need to not do anything */
  875.       if (selected!=null) {
  876.     /* transform the data into something with indexes*/
  877.     for (i=0; i<selected.size();++i) {
  878.       /* grab the individual element */
  879.       obj=(interactor)selected.elementAt(i);
  880.       /* see if its in the child list */
  881.       index=_child_display.find_child(obj);
  882.       /* if its not -1, we add it */
  883.       if (index!=-1) {
  884.         indexes.addElement(new Integer(index));
  885.       } else {
  886.         System.out.println("HM. Index probably shouldn't be -1");
  887.       }
  888.     }
  889.       }
  890.       /* forward this message on to others */
  891.       next.set_selected_by_index(indexes);
  892.       /* we are done synchronizing */
  893.       synch_in_progress=false;
  894.     }
  895.     /* we never want a double click to happen if we have changed
  896.      * what is selected.
  897.      */
  898.     _double_click_trans=null;
  899.     /* we need to clear the focused on item */
  900.     set_focused(-1);
  901.     /* just do the superclass behavior */
  902.     super.set_selected(selected);
  903.   }
  904.   /**
  905.    * We override this method to make sure that we keep the listbox
  906.    * selection state synchronized.
  907.    * @param Vector selected a vector of Integer objects
  908.    */
  909.   public void set_selected_by_index(Vector selected) {
  910.  
  911.     if ((synch_mask() & SYNCH_SELECTION) !=0) {
  912.       /* we need to do it */
  913.       if (synch_in_progress) {
  914.     /* end of the road for this puppy */
  915.     return;
  916.       }
  917.       /* need to remember where we have been */
  918.       synch_in_progress=true;
  919.       /* tell the next guy */
  920.       next.set_selected_by_index(selected);
  921.       /* we are done synchronizing */
  922.       synch_in_progress=false;
  923.     }
  924.     /* we need to clear the focused on item */
  925.     set_focused(-1);
  926.     /* do the superclass behavior */
  927.     super.set_selected_by_index(selected);
  928.   }
  929.   /**
  930.    * We override this function to allow synchronization of the 
  931.    * listboxes.
  932.    * @param int y the coordinate to display
  933.    */
  934.   public void set_scrolled_y(int y) {
  935.     if ((synch_mask() & SYNCH_Y_COORD) !=0) {
  936.       /* we need to do it */
  937.       if (synch_in_progress) {
  938.     /* end of the road for this puppy */
  939.     return;
  940.       }
  941.       /* need to remember where we have been */
  942.       synch_in_progress=true;
  943.       /* tell the next guy */
  944.       next.set_scrolled_y(y);
  945.       /* we are done synchronizing */
  946.       synch_in_progress=false;
  947.     }
  948.     /* just do superclass behavior */
  949.     super.set_scrolled_y(y);
  950.   }
  951.   /**
  952.    * We override this function to allow synchronization of the 
  953.    * listboxes.
  954.    * @param int x the x coordinate to display
  955.    */
  956.   public void set_scrolled_x(int x) {
  957.     if ((synch_mask() & SYNCH_X_COORD) !=0) {
  958.       /* we need to do it */
  959.       if (synch_in_progress) {
  960.     /* end of the road for this puppy */
  961.     return;
  962.       }
  963.       /* need to remember where we have been */
  964.       synch_in_progress=true;
  965.       /* tell the next guy */
  966.       next.set_scrolled_x(x);
  967.       /* we are done synchronizing */
  968.       synch_in_progress=false;
  969.     }
  970.     /* just do superclass behavior */
  971.     super.set_scrolled_x(x);
  972.   }
  973.   /**
  974.    * This method gets called when the double click timer expires. This
  975.    * method clears the state variable so we know that subsequent clicks
  976.    * are not part of a double click.  It also fires the callback to
  977.    * inform the userlevel code what happened.
  978.    * @param event e the animation event which caused time to expire
  979.    */
  980.   public void time_expired(event evt) {
  981.     _double_click_trans=null;
  982.     /* run the callback */
  983.     single_click_final_hook(evt);
  984.   }
  985.   /**
  986.    * This function is called by the listbox if it detects a second
  987.    * click in a short period of time on the object just released
  988.    * on. <P>
  989.    * 
  990.    * The user should be aware that the default behavior of this
  991.    * code is to run a callback (DOUBLE_CLICK). If you override
  992.    * this method, you will not get the call to the callback
  993.    * unless you call super. Further, most users shouldn't
  994.    * need to override this as the can put their response in 
  995.    * the callback object. <P>
  996.    *
  997.    * When you get a double click the parameter passed to the callback
  998.    * function is the element which was double clicked on. It is
  999.    * returned via the convert_to_object() part of the list_element
  1000.    * interface.
  1001.    *
  1002.    * @param event evt the event in question
  1003.    * @param Object user_info (not currently used)
  1004.    */
  1005.   protected void double_click_hook(event evt, Object user_info) {
  1006.     Vector v;
  1007.     /* is there a callback object?*/
  1008.     if (callback_obj()!=null) {
  1009.       /* grab the selected set */
  1010.       v=contents();
  1011.       callback_obj().callback(this,evt,DOUBLE_CLICK,
  1012.                   v.elementAt(_double_click_loc));
  1013.     }
  1014.   }
  1015.   /**
  1016.    * This function is called by the listbox code when it detects that
  1017.    * the currently focused item has been pressed on. <P>
  1018.    *
  1019.    * The user should be aware that the default behavior of this
  1020.    * code is to run a callback (DRAG). If you override
  1021.    * this method, you will not get the call to the callback
  1022.    * unless you call super. Further, most users shouldn't
  1023.    * need to override this as the can put their response in 
  1024.    * the callback object.<P>
  1025.    *
  1026.    * When you get a drag callback the parameter passed to the callback
  1027.    * function is the element which was dragged. It is
  1028.    * returned via the convert_to_object() part of the list_element
  1029.    * interface.
  1030.    *
  1031.    * @param event evt the event in question
  1032.    * @param Object user_info (not currently used)
  1033.    */
  1034.   protected void drag_hook(event evt, Object user_info) {
  1035.     Vector v;
  1036.  
  1037.     /* if we have already sent this message, then we want to abort */
  1038.     if (drag_message_sent) return;
  1039.     /* is there a callback object?*/
  1040.     if (callback_obj()!=null) {
  1041.       /* grab the selected set */
  1042.       v=contents();
  1043.       callback_obj().callback(this,evt,DRAG,
  1044.                   v.elementAt(drag_focus_index));
  1045.     }
  1046.     /* we have sent the message ... note: we should not set the state
  1047.      * of the drag_message sent until AFTER we have really called the
  1048.      * callback as it may examine this value during the callback itself */
  1049.     drag_message_sent=true;
  1050.   }
  1051.   /**
  1052.    * If the user is in the middle of a double click and the contents
  1053.    * change we don't want the double click to happen. 
  1054.    *
  1055.    * @param Vector contents the new contents of this object (all the objects in this vector must be list_elements)
  1056.    */
  1057.   public void set_contents(Vector contents) {
  1058.     _double_click_trans=null;
  1059.     /* we need to clear the focused on item */
  1060.     set_focused(-1);
  1061.     super.set_contents(contents);
  1062.   }
  1063.  
  1064.    //had:
  1065.    //* @exception general PROPAGATED
  1066.  
  1067.   /**
  1068.    * This function is called when the listbox is sure that no further
  1069.    * click is forthcoming and that the callback code for the 
  1070.    * SINGLE_CLICK_FINAL can be safely called.<P>
  1071.    *
  1072.    * Most users shouldn't need to override this method, as they can
  1073.    * put their code to handle the single click in the callback object.<P>
  1074.    *
  1075.    * The object passed as the parameter to the callback function
  1076.    * when this callback is run is the currently focused on
  1077.    * object. The event field of the callback may be animation
  1078.    * event if this callback is run in response to a timeout rather
  1079.    * than a user input. 
  1080.    * @param event evt the event causing this hook to be run
  1081.    */
  1082.   protected void single_click_final_hook(event evt) {
  1083.     Vector v;
  1084.     /* is there a callback object?*/
  1085.     if (callback_obj()!=null) {
  1086.       /* grab the selected set */
  1087.       v=contents();
  1088.       callback_obj().callback(this,evt,SINGLE_CLICK_FINAL,
  1089.                   v.elementAt(_focused));
  1090.     }
  1091.   }
  1092.   /**
  1093.    * This function is called when the user clicks the mouse
  1094.    * button on an item. It is called at the time the user
  1095.    * clicks, so there may be a double click coming down the pipe. <P>
  1096.    * 
  1097.    * The parameter passed to callback function is the currently
  1098.    * focused on item.
  1099.    */
  1100.   protected void single_click_pending_hook(event evt) {
  1101.     Vector v;
  1102.     /* is there a callback object?*/
  1103.     if (callback_obj()!=null) {
  1104.       /* grab the selected set */
  1105.       v=contents();
  1106.       callback_obj().callback(this,evt,SINGLE_CLICK_PENDING,
  1107.                   v.elementAt(_focused));
  1108.     }
  1109.   }
  1110.   /**
  1111.    * This function is called to determine if a drag is now beginning.
  1112.    * It makes that determination based on how far the mouse has moved
  1113.    * from the drag origin.
  1114.    *
  1115.    * It is called by the drag_feedback functions.
  1116.    * @param int local_x current x coordinate
  1117.    * @param int local_y current y coordinate
  1118.    */
  1119.   public boolean drag_ready(int local_x, int local_y) {
  1120.     int large_x, small_x, large_y, small_y;
  1121.  
  1122.     /* setup the big and small x values */
  1123.     if (local_x>drag_x_origin) {
  1124.       large_x=local_x;
  1125.       small_x=drag_x_origin;
  1126.     } else {
  1127.       large_x=drag_x_origin;
  1128.       small_x=local_x;
  1129.     }
  1130.     /* setup the big and small y values */
  1131.     if (local_y>drag_y_origin) {
  1132.       large_y=local_y;
  1133.       small_y=drag_y_origin;
  1134.     } else {
  1135.       large_y=drag_y_origin;
  1136.       small_y=local_y;
  1137.     }
  1138.     /* is it far enough? */
  1139.     if ((large_x-small_x >= DRAG_DISTANCE) ||
  1140.     (large_y-small_y>= DRAG_DISTANCE)) {
  1141.       /* tell the caller we're ready for a drag */
  1142.       return true;
  1143.     }
  1144.     /* not far enough */
  1145.     return false;
  1146.   }
  1147. }
  1148.   
  1149.  
  1150.  
  1151. /*=========================== COPYRIGHT NOTICE ===========================
  1152.  
  1153. This file is part of the subArctic user interface toolkit.
  1154.  
  1155. Copyright (c) 1996 Scott Hudson and Ian Smith
  1156. All rights reserved.
  1157.  
  1158. The subArctic system is freely available for most uses under the terms
  1159. and conditions described in 
  1160.   http://www.cc.gatech.edu/gvu/ui/sub_arctic/sub_arctic/doc/usage.html 
  1161. and appearing in full in the lib/interactor.java source file.
  1162.  
  1163. The current release and additional information about this software can be 
  1164. found starting at: http://www.cc.gatech.edu/gvu/ui/sub_arctic/
  1165.  
  1166. ========================================================================*/
  1167.